﻿using Collections = System.Collections.Generic;
using System.Collections.Generic;
using IO = System.IO;
using Text = System.Text;

namespace pulsarc.Language
{
    public class StringLiteral
    {
        public string Value;
    }
    public class Scanner
    {
        int line = 0;
        private readonly Collections.IList<object> result;
        public static List<List<int>> Lines = new List<List<int>>();
        #region " Opperands "
        public static readonly object Add = new object();
        public static readonly object Sub = new object();
        public static readonly object Mul = new object();
        public static readonly object Div = new object();
        public static readonly object Semi = new object();
        public static readonly object Assign = new object();
        public static readonly object OpenBracket = new object();
        public static readonly object CloseBracker = new object();
        public static readonly object OpenPar = new object();
        public static readonly object ClosePar = new object();
        public static readonly object Comma = new object();
        public static readonly object EOL = new object();
        public static readonly object Dot = new object();
        public static readonly object And = new object();
        public static readonly object Equal = new object();
        public static readonly object NotEqual = new object();
        public static readonly object Colon = new object();
        public static readonly object At = new object();
        public static readonly object GreaterThan = new object();
        public static readonly object LessThen = new object();
        public static readonly object PlusEqual = new object();
        #endregion
        public Scanner(IO.TextReader input)
        {
            this.result = new Collections.List<object>();
            this.Scan(input);
        }
        public Collections.IList<object> Tokens
        {
            get { return this.result; }
        }
        /// <summary>
        /// Scans for tokens.
        /// </summary>
        /// <param name="input"></param>
        private void Scan(IO.TextReader input)
        {
            Lines = new List<List<int>>();
            Lines.Add(new List<int>());
            while (input.Peek() != -1)
            {
                char ch = (char)input.Peek();
                Lines[line].Add(result.Count);
                if (ch == '#')
                {
                    while (ch != '\n')
                    {
                        ch = (char)input.Read();
                    }
                }
                else if (ch == '\n')
                {
                    input.Read();
                    Lines.Add(new List<int>());
                    line++;

                }
                else if (char.IsWhiteSpace(ch))
                {
                    input.Read();
                }

                else if (char.IsLetter(ch) || ch == '_')
                {
                    Text.StringBuilder accum = new Text.StringBuilder();

                    while (char.IsLetter(ch) || char.IsNumber(ch))
                    {
                        accum.Append(ch);
                        input.Read();

                        if (input.Peek() == -1)
                        {
                            break;
                        }
                        else
                        {
                            ch = (char)input.Peek();
                        }
                    }
                    this.result.Add(accum.ToString());
                }
                else if (ch == '\'')
                {
                    input.Read();
                    result.Add((char)input.Read());
                    if (((char)input.Read()) != '\'')
                    {
                        throw new System.Exception("Expected '");
                    }

                }
                else if (ch == '"')
                {
                    Text.StringBuilder accum = new Text.StringBuilder();

                    input.Read(); // skip the '"'

                    if (input.Peek() == -1)
                    {
                        throw new System.Exception("Expected \"");
                    }

                    while ((ch = (char)input.Peek()) != '"')
                    {
                        accum.Append(ch);
                        input.Read();

                        if (input.Peek() == -1)
                        {
                            throw new System.Exception("Expected \"");
                        }
                    }
                    StringLiteral sl = new StringLiteral();
                    input.Read();
                    sl.Value = accum.ToString();
                    this.result.Add(sl);
                }
                else if (char.IsDigit(ch))
                {
                    // numeric literal

                    Text.StringBuilder accum = new Text.StringBuilder();

                    while (char.IsDigit(ch))
                    {
                        accum.Append(ch);
                        input.Read();

                        if (input.Peek() == -1)
                        {
                            break;
                        }
                        else
                        {
                            ch = (char)input.Peek();
                        }
                    }

                    this.result.Add(int.Parse(accum.ToString()));
                }
                else switch (ch)
                    {
                        case '+':
                            input.Read();
                            if (input.Peek() == (int)'=')
                            {
                                input.Read();
                                this.result.Add(Scanner.PlusEqual);
                                break;
                            }
                            this.result.Add(Scanner.Add);
                            break;

                        case '-':
                            input.Read();
                            this.result.Add(Scanner.Sub);
                            break;

                        case '*':
                            input.Read();
                            this.result.Add(Scanner.Mul);
                            break;
                        case '@':
                            input.Read();
                            this.result.Add(Scanner.At);
                            break;
                        case '/':
                            input.Read();
                            this.result.Add(Scanner.Div);
                            break;
                        case '!':
                            input.Read();
                            if ((char)(input.Peek()) == '=')
                            {
                                input.Read();
                                this.result.Add(Scanner.NotEqual);
                            }
                            break;
                        case '=':
                            input.Read();
                            if (((char)input.Peek()) == '=')
                            {
                                input.Read();
                                this.result.Add(Scanner.Equal);
                            }
                            else
                                this.result.Add(Scanner.Assign);
                            break;

                        case ';':
                            input.Read();
                            this.result.Add(Scanner.Semi);
                            break;
                        case '[':
                            input.Read();
                            this.result.Add(Scanner.OpenBracket);
                            break;
                        case ']':
                            input.Read();
                            this.result.Add(Scanner.CloseBracker);
                            break;
                        case '(':
                            input.Read();
                            this.result.Add(Scanner.OpenPar);
                            break;
                        case '<':
                            input.Read();
                            this.result.Add(Scanner.LessThen);
                            break;
                        case '>':
                            input.Read();
                            this.result.Add(Scanner.GreaterThan);
                            break;
                        case ')':
                            input.Read();
                            this.result.Add(Scanner.ClosePar);
                            break;
                        case ',':
                            input.Read();
                            this.result.Add(Scanner.Comma);
                            break;
                        case '.':
                            input.Read();
                            this.result.Add(Scanner.Dot);
                            break;
                        case '&':
                            input.Read();
                            this.result.Add(Scanner.And);
                            break;
                        case ':':
                            input.Read();
                            this.result.Add(Scanner.Colon);
                            break;
                        default:
                            throw new System.Exception("Scanner encountered unrecognized character '" + ch + "'");
                    }

            }
        }
    }
}
